/*
Copyright (c) 2010 julien barbay <barbay.julien@gmail.com>

 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
 files (the "Software"), to deal in the Software without
 restriction, including without limitation the rights to use,
 copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the
 Software is furnished to do so, subject to the following
 conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.
*/

//TODO
package martian.m4gic.services
{
	import flash.display.*;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	import martian.m4gic.data.Ini;
	import martian.m4gic.display.Smartie;
	import martian.m4gic.draw;
	import martian.m4gic.tools.Domain;
	
	public class Assets
	{
		public function Assets() {}
		
		static public function create(cls:Class, params:Object):*
		{
			var ret:*;
			
			try { ret = new cls(); }
			catch (e:ArgumentError) { return null; }
			if (params == null) { return ret; }
			
			if (params.parent != null)
			{
				if (params.parent is DisplayObjectContainer)
				{
					params.parent.addChild(ret);
					delete params.parent;
				}
			}
			
			return Ini.dump(params, ret);
		}
		
		/**
		 * @desc converts a displayable class into an other with blittering it into a bitmapdata
		 */
		static public function convert(cls:Class, type:Class = null):*
		{
			type = (type != null) ? type : Smartie;
			
			var ret:*;
			
			if (cls == type)
			{	
				try { ret = new cls(); }
				catch (e:ArgumentError) { ret = null; }
			}
			else if (Domain.extending(type, BitmapData)) { ret = transform(cls); }
			else { ret = prepare(transform(cls), type); }
			
			return ret;
		}
		
		/**
		 * @desc draw the given bitmapdata in the displayobject class you want and return it
		 */
		static public function prepare(bmd:BitmapData, type:Class = null):*
		{
			type = (type != null) ? type : Smartie;
			
			var ast:*;
			
			if (Domain.extending(type, Bitmap)) { ast = new type(bmd, PixelSnapping.AUTO, true); }
			else if (Domain.extending(type, Shape) || Domain.extending(type, Sprite))
			{
				ast = new type();
					ast.graphics.beginBitmapFill(bmd, null, false, true);
					ast.graphics.drawRect(0, 0, bmd.width, bmd.height);
					ast.graphics.endFill();
			}
				
			return ast;
		}
		
		static public function replace(object:DisplayObject):Smartie
		{
			if (object.parent == null) { throw new Error("the object must be added to a displaylist"); }
			
			var smartie:Smartie = new Smartie(),
				parent:* = object.parent,
				depth:int = parent.getChildIndex(object);
			
			for (var property:* in object) { smartie[property] = object[property]; }
				dump(object, smartie);
			
			parent.addChild(smartie);
			parent.swapChildren(object, smartie);
			parent.removeChild(object);
				
			return smartie;
		}
		
		/**
		 * @desc move all the children of a displayobject to an other, with creating them as property if possible
		 */
		static public function dump(from:*, to:*, offset:Point = null):Boolean
		{
			if (offset == null) { offset = new Point(); }
			if (!Domain.extending(from, DisplayObjectContainer) || !Domain.extending(to, DisplayObjectContainer)) { return false; }
			
			var dynamic:Boolean = Domain.dynamic(to);
			
			var dump:* = from is Class ? new from() : from,
				children:Array = new Array(),
				child:DisplayObject;
			
			if (dump.numChildren == 0) { return true; }
			for (var i:int = 0; i < dump.numChildren; i++) { children.push(dump.getChildAt(i)); }
			
			try
			{
				for (var j:int = 0; j < children.length; j++)
				{
					child = children[j];
					child.x += offset.x;
					child.y += offset.y;
					
					if (dynamic || to[child.name] != undefined) { to[child.name] = to.addChild(child); }
					else { to.addChild(child); }
				}
				
				return true;
			}
			catch(e:Error) {}
			
			return false;
		}
		
		/**
		 * @desc flushes the displaylist of the container
		 */
		static public function flush(object:DisplayObjectContainer):void
		{
			while(object.numChildren > 0) { object.removeChildAt(object.numChildren - 1); }
		}
		
		/**
		 * @desc transforms almost any displayobject to a BitmapData
		 */
		static public function transform(cls:Class):BitmapData
		{
			var bmd:BitmapData;
			
			if (Domain.extending(cls, BitmapData))
			{
				try { bmd = new cls(); }
				catch (e:ArgumentError) { bmd = new cls(1, 1); }
			}
			else if (Domain.extending(cls, Bitmap)) { bmd = Bitmap(new cls()).bitmapData; }
			else if (Domain.implementing(cls, IBitmapDrawable))
			{
				var mc:* = new cls();
				
				bmd = new BitmapData(cls.width, cls.height, true, 0);
					bmd.draw(mc);
			}
			
			return bmd;
		}
		
		
		
		
		
		
		
		
		
		
		/**
		 * @return a black and white placeholder with a cross, usefull for prototypes
		 */
		static public function placeholder(w:int, h:int):Vector.<IGraphicsData>	
		{
			var stroke:GraphicsStroke = new GraphicsStroke(1);
				stroke.fill = new GraphicsSolidFill(0, 1);
			
			var fill:GraphicsSolidFill = new GraphicsSolidFill(0xFFFFFF, 1);
			
			var path:GraphicsPath = new GraphicsPath(new Vector.<int>(), new Vector.<Number>());
				path.commands.push(1, 2, 2, 2, 2, 2, 1, 2);
				path.data.push(0, 0, w, 0, w, h, 0, h, 0, 0, w, h, w, 0, 0, h);
				
			var drawing:Vector.<IGraphicsData> = new Vector.<IGraphicsData>();
				drawing.push(stroke, fill, path);
			
			return drawing;
		}
		
		/**
		 * @return a square fill command
		 */
		static public function rect(args:Object):Vector.<IGraphicsData>
		{
			var ini:Ini = new Ini(args),
				w:int = ini.number("width", 100),
				h:int = ini.number("height", 100),
				color:int = ini.integer("color", 0xffffff),
				alpha:int = ini.number("alpha", 1);
			
			return draw(null).fill(color, alpha).rect(0, 0, w, h).cmd;
		}
		
		/**
		 * @return a cyan placeholder usefull for masks
		 */
		static public function mask(object:DisplayObjectContainer, parent:Boolean = false):Smartie
		{
			var mask:Smartie = new Smartie();
				mask.name = "mask";
				draw(mask.graphics).fill(0, 0).rect(object.width, object.height).apply().release();
			
			object.mask = mask;
				
			if (parent) { object.parent.addChild(mask); }
			else { object.addChild(mask); }
				
			return mask;
		}
		
		/**
		 * @return 10px² translucent BitmapData with first pixel black
		 */
		static public function get grid():BitmapData
		{
			var bmd:BitmapData = new BitmapData(10, 10, true, 0);
				bmd.setPixel32(0, 0, 0xFF000000);
				
			return bmd;
		}
		
		/**
		 * @return 20px² BitmapData with 75%alpha/25%alpha black tiles
		 */
		static public function get pattern():BitmapData
		{
			var bmd:BitmapData = new BitmapData(20, 20, true, 0xC0000000);
				bmd.fillRect(new Rectangle(10,  0, 10, 10), 0x40000000);
				bmd.fillRect(new Rectangle( 0, 10, 10, 10), 0x40000000);
			
			return bmd;				
		}
	}
}